package a01;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.io.Resource;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Locale;
import java.util.Map;

/*
    BeanFactory 与 ApplicationContext 的区别
 */
@SpringBootApplication
public class A01 {

    private static final Logger log = LoggerFactory.getLogger(A01.class);


    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {

        /**
         * 什么是BeanFactory? 什么是ApplicationContext
         * 如下，SpringApplication.run返回的就是一个ApplicationContext，下面这个是一个接口。
         * 打开他的类继承图，可以看到其实继承了BeanFactory接口，也就是说拥有BeanFactory的接口，而
         * BeanFactory接口里方法不多，包括我们常用的getBean。，那context里的这个方法是怎么实现的
         * 呢。看方法的实现可以知道，是先获得BeanFactory，再通过BeanFactory的getBean去获得类的。
         * 可见context是组合使用了BeanFactory.（BeanFactory是其成员变量）
         * BeanFactory才是核心。可见，虽然BeanFactory接口方法不多，但它的实现类提供了众多的功能。
         * 包括控制反转、基本的依赖注入、Bean的各种生命周期（这句话暂时理解不深）
         */
        ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
        System.out.println(context);
        Object bean = context.getBean("component01");


        /**
         * debug可知，上面获得的context的BeanFactory是DefaultListableBeanFactory，其继承的一个
         * 类是DefaultSingletonBeanRegistry，其有一个属性singletonObjects可以看到各个单例Bean
         * 这里要看的话debug当然可以看，不过这里练习使用一下反射，使用反射来获取
         */
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
        singletonObjects.setAccessible(true);
        Map<String, Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);
        map.keySet().stream().filter(s -> s.startsWith("component")).
                forEach(s -> System.out.println(s + "=" + map.get(s)));


        /**
         * ConfigurableApplicationContext继承了接口包括：MessageSource、ResourcePatternResolver、
         * EnvironmentCapable、ApplicationEventPublisher。下面一一来介绍他们的作用
         */

        /**
         * MessageSource 国际化资源.现在看来，这个资源文件以messages开头，后接语言简写。感觉这个简单了解
         * 即可
         */
        System.out.println(context.getMessage("hi", null, Locale.CHINESE));
        System.out.println(context.getMessage("hi", null, Locale.JAPANESE));

        /**
         * ResourcePatternResolver 根据路径匹配资源文件
         * Spring中资源文件用Resource这个接口抽象
         */
        //获取类路径下以 .properties结尾的文件
        Resource[] resources = context.getResources("classpath:*.properties");
        for (Resource resource : resources) {
            System.out.println(resource);
        }

        //获取各个boot依赖类路径下的spring.factories
        Resource[] resources1 = context.getResources("classpath*:META-INF/spring.factories");
        for (Resource resource : resources1) {
            System.out.println(resource);
        }

        /**
         * EnvironmentCapable 获取环境变量、配置文件中的变量值
         */
        System.out.println(context.getEnvironment().getProperty("java_home"));
        System.out.println(context.getEnvironment().getProperty("server.port"));

        /**
         * ApplicationEventPublisher 用于事件发布
         */
        Component01 component01 = context.getBean(Component01.class);
        component01.register();


    }
}
